#ifndef RCMD_RECALL_UTILS_HPP
#define RCMD_RECALL_UTILS_HPP

#include <define.h>
#include <string>
#include <vector>
#include <sstream>
#include <json.hpp>
#include <sys/types.h>
#include <sys/stat.h>
#include <iostream> /* cout */
#include <unistd.h>/* gethostname */
#include <netdb.h> /* struct hostent */
#include <arpa/inet.h> /* inet_ntop */
#include <iostream>
#include <sys/types.h>
#include <sys/socket.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <sys/fcntl.h>/* file lock */
#include <arpa/inet.h>


#define countof(x) sizeof(x) / sizeof(x[0])
#define zeromem(x) memset(&x, 0, sizeof(x))
using namespace std;
using namespace nlohmann;
class StringUtils{
public:
    static std::vector<std::string> split(const std::string& s, char delimiter)
    {
        std::vector<std::string> tokens;
        std::string token;
        std::istringstream tokenStream(s);
        while (std::getline(tokenStream, token, delimiter))
        {
            tokens.push_back(token);
        }
        return tokens;
    }

    static string stringify_array(const vector<string>& s){
        string str;
        for (int i = 0;i < s.size(); i ++){
            str += s[i];
            if (i < s.size() - 1){
                str += ",";
            }
        }
        return str;
    }

};

class TimeUtils{
public:
    static uint32_t stat_time_cost(timeval* start){
        if (start->tv_sec == 0){
            gettimeofday(start, NULL);
            return 0;
        }
        else{
            timeval now;
            gettimeofday(&now, NULL);
            return (now.tv_sec - start->tv_sec) * 1000 + (now.tv_usec - start->tv_usec) / 1000;
        }
    }

    static uint32_t now(){
        timeval now;
        gettimeofday(&now, NULL);
        return now.tv_sec;
    }
};
class HttpUtils{
public:
    static void compose_response(int retcode,const string& retmsg, const string&  uid, const json& data, json& resp){
        resp["error"] = json();
        if (retcode){
            resp["error"]["code"] = retcode;
            resp["error"]["message"] = retmsg;
        }
        resp["data"] = data;
        resp["utime"] = TimeUtils::now();
        resp["uid"] = uid;
    }

/*
    static int get_host_ip(std::string& host_name, std::string& ip) {
        char name[256];
        gethostname(name, sizeof(name));
        host_name = name;

        struct hostent* host = gethostbyname(name);
        char ipStr[32];
        const char* ret = inet_ntop(host->h_addrtype, host->h_addr_list[0], ipStr, sizeof(ipStr));
        if (NULL==ret) {
            return -1;
        }
        ip = ipStr;
        return 0;
    }
*/
#ifdef __linux__
    #include <linux/if.h>
    static int get_host_ip(string net_name, string &strIP)
    {
        const int BUF_SIZE = 1024;
        int sock_fd;
        struct ifconf conf;
        struct ifreq *ifr;
        char buff[BUF_SIZE] = {0};
        int num;
        int i;

        sock_fd = socket(PF_INET, SOCK_DGRAM, 0);
        if ( sock_fd < 0 )
            return -1;

        conf.ifc_len = BUF_SIZE;
        conf.ifc_buf = buff;

        if ( ioctl(sock_fd, SIOCGIFCONF, &conf) < 0 )
        {
            close(sock_fd);
            return -1;
        }

        num = conf.ifc_len / sizeof(struct ifreq);
        ifr = conf.ifc_req;

        for(i = 0; i < num; i++)
        {
            struct sockaddr_in *sin = (struct sockaddr_in *)(&ifr->ifr_addr);

            if ( ioctl(sock_fd, SIOCGIFFLAGS, ifr) < 0 )
            {
                close(sock_fd);
                return -1;
            }

            if ( (ifr->ifr_flags & IFF_UP) && strcmp(net_name.c_str(),ifr->ifr_name) == 0 )
            {
                strIP = inet_ntoa(sin->sin_addr);
                close(sock_fd);

                return 0;
            }

            ifr++;
        }

        close(sock_fd);

        return -1;
    }
#endif
    static unsigned char to_hex(unsigned char x)
    {
        return  x > 9 ? x + 55 : x + 48;
    }

    static unsigned char from_hex(unsigned char x)
    {
        unsigned char y;
        if (x >= 'A' && x <= 'Z') y = x - 'A' + 10;
        else if (x >= 'a' && x <= 'z') y = x - 'a' + 10;
        else if (x >= '0' && x <= '9') y = x - '0';
        else assert(0);
        return y;
    }

    static std::string url_encode(const std::string& str)
    {
        std::string strTemp = "";
        size_t length = str.length();
        for (size_t i = 0; i < length; i++)
        {
            if (isalnum((unsigned char)str[i]) ||
                (str[i] == '-') ||
                (str[i] == '_') ||
                (str[i] == '.') ||
                (str[i] == '~'))
                strTemp += str[i];
            else if (str[i] == ' ')
                strTemp += "+";
            else
            {
                strTemp += '%';
                strTemp += to_hex((unsigned char) str[i] >> 4);
                strTemp += to_hex((unsigned char) str[i] % 16);
            }
        }
        return strTemp;
    }

    static std::string url_decode(const std::string& str)
    {
        std::string strTemp = "";
        size_t length = str.length();
        for (size_t i = 0; i < length; i++)
        {
            if (str[i] == '+') strTemp += ' ';
            else if (str[i] == '%')
            {
                assert(i + 2 < length);
                unsigned char high = from_hex((unsigned char) str[++i]);
                unsigned char low = from_hex((unsigned char) str[++i]);
                strTemp += high*16 + low;
            }
            else strTemp += str[i];
        }
        return strTemp;
    }
};
class FileUtils{
public:
    static long get_modify_time(const char* file_path){
        FILE* fp = fopen(file_path, "r");
        if(fp == NULL)
        {
            return -1;
        }
        int fd = 0;
        struct stat buf;
        int ret = 0;
        fd = fileno(fp);
        if(fd <= 0){
            fclose(fp);
            return -2;
        }
        fstat(fd, &buf);
        long modify_time = buf.st_mtime;
        if (modify_time == 0){
            fclose(fp);
            return -3;
        }
        fclose(fp);
        return modify_time;
    }

    static int lock_file(const char* file_lock){
        int fd = open(file_lock, O_RDWR|O_CREAT);
        if (fd == -1) {
            return -1;
        }

        // 锁住整个文件
        struct flock lock = {};
        lock.l_type = F_WRLCK;
        lock.l_whence = SEEK_SET;
        lock.l_start = 0;
        lock.l_len = 0;

        lock.l_pid = getpid();

        if (fcntl(fd, F_SETLK, &lock) == -1) {
            return -2;
        }

        return 0;
    }

    static int unlock_file(struct flock* lock, int fd){
        lock->l_type = F_UNLCK;
        if (fcntl(fd, F_SETLK, &lock) == -1) {
            return -1;
        }
        return 0;
    }
};

#include <stdio.h>
#include <unistd.h>
#include <sys/time.h>
#include <string.h>
#include <stdlib.h>

#define VMRSS_LINE 17
#define VMSIZE_LINE 13
#define PROCESS_ITEM 14

typedef struct {
    unsigned long user;
    unsigned long nice;
    unsigned long system;
    unsigned long idle;
}Total_Cpu_Occupy_t;


typedef struct {
    unsigned int pid;
    unsigned long utime;  //user time
    unsigned long stime;  //kernel time
    unsigned long cutime; //all user time
    unsigned long cstime; //all dead time
}Proc_Cpu_Occupy_t;


class SystemUtils{
public:
//获取第N项开始的指针
    static const char* get_items(const char*buffer ,unsigned int item){

        const char *p =buffer;

        int len = strlen(buffer);
        int count = 0;

        for (int i=0; i<len;i++){
            if (' ' == *p){
                count ++;
                if(count == item -1){
                    p++;
                    break;
                }
            }
            p++;
        }

        return p;
    }


//获取总的CPU时间
    static unsigned long get_cpu_total_occupy(){

        FILE *fd;
        char buff[1024]={0};
        Total_Cpu_Occupy_t t;

        fd =fopen("/proc/stat","r");
        if (nullptr == fd){
            return 0;
        }

        fgets(buff,sizeof(buff),fd);
        char name[64]={0};
        sscanf(buff,"%s %ld %ld %ld %ld",name,&t.user,&t.nice,&t.system,&t.idle);
        fclose(fd);

        return (t.user + t.nice + t.system + t.idle);
    }


//获取进程的CPU时间
    static unsigned long get_cpu_proc_occupy(unsigned int pid){

        char file_name[64]={0};
        Proc_Cpu_Occupy_t t;
        FILE *fd;
        char line_buff[1024]={0};
        sprintf(file_name,"/proc/%d/stat",pid);

        fd = fopen(file_name,"r");
        if(nullptr == fd){
            return 0;
        }

        fgets(line_buff,sizeof(line_buff),fd);

        sscanf(line_buff,"%u",&t.pid);
        const char *q =get_items(line_buff,PROCESS_ITEM);
        sscanf(q,"%ld %ld %ld %ld",&t.utime,&t.stime,&t.cutime,&t.cstime);
        fclose(fd);

        return (t.utime + t.stime + t.cutime + t.cstime);
    }


//获取CPU占用率
    static float get_proc_cpu(unsigned int pid){

        unsigned long totalcputime1,totalcputime2;
        unsigned long procputime1,procputime2;

        totalcputime1=get_cpu_total_occupy();
        procputime1=get_cpu_proc_occupy(pid);

        usleep(200000);

        totalcputime2=get_cpu_total_occupy();
        procputime2=get_cpu_proc_occupy(pid);

        float pcpu = 0.0;
        if(0 != totalcputime2-totalcputime1){
            pcpu=100.0 * (procputime2-procputime1)/(totalcputime2-totalcputime1);
        }

        return pcpu;
    }


//获取进程占用内存
    static unsigned int get_proc_mem(unsigned int pid){

        char file_name[64]={0};
        FILE *fd;
        char line_buff[512]={0};
        sprintf(file_name,"/proc/%d/status",pid);

        fd =fopen(file_name,"r");
        if(nullptr == fd){
            return 0;
        }

        char name[64];
        int vmrss;
        for (int i=0; i<VMRSS_LINE-1;i++){
            fgets(line_buff,sizeof(line_buff),fd);
        }

        fgets(line_buff,sizeof(line_buff),fd);
        sscanf(line_buff,"%s %d",name,&vmrss);
        fclose(fd);

        return vmrss;
    }


//获取进程占用虚拟内存
    static unsigned int get_proc_virtualmem(unsigned int pid){

        char file_name[64]={0};
        FILE *fd;
        char line_buff[512]={0};
        sprintf(file_name,"/proc/%d/status",pid);

        fd =fopen(file_name,"r");
        if(nullptr == fd){
            return 0;
        }

        char name[64];
        int vmsize;
        for (int i=0; i<VMSIZE_LINE-1;i++){
            fgets(line_buff,sizeof(line_buff),fd);
        }

        fgets(line_buff,sizeof(line_buff),fd);
        sscanf(line_buff,"%s %d",name,&vmsize);
        fclose(fd);

        return vmsize;
    }


//进程本身
    static int get_pid(const char* process_name, const char* user = nullptr)
    {
        if(user == nullptr){
            user = getlogin();
        }

        char cmd[512];
        if (user){
            sprintf(cmd, "pgrep %s -u %s", process_name, user);
        }

        FILE *pstr = popen(cmd,"r");

        if(pstr == nullptr){
            return 0;
        }

        char buff[512];
        ::memset(buff, 0, sizeof(buff));
        if(NULL == fgets(buff, 512, pstr)){
            return 0;
        }

        return atoi(buff);
    }
};

#endif //RCMD_RECALL_UTILS_HPP